import redis
import json
from redis.asyncio import Redis as AsyncRedis
import redis.asyncio
from loguru import logger

class RedisClient:
    """Redis客户端单例类,提供Redis基本操作的封装"""
    _instances = {}

    def __new__(cls, **kwargs):
        """单例模式实现,确保相同配置只创建一个实例"""
        key = (kwargs.get('host', '127.0.0.1'), kwargs.get('port', 6379), kwargs.get('db', 0))
        if key not in cls._instances:
            cls._instances[key] = super(RedisClient, cls).__new__(cls)
        return cls._instances[key]

    def __init__(self, **kwargs):
        """初始化Redis连接池和客户端"""
        if not hasattr(self, 'initialized'):
            host = kwargs.get('host', '127.0.0.1')
            port = kwargs.get('port', 6379)
            db = kwargs.get('db', 0)
            password = kwargs.get('password', None)
            max_connections = kwargs.get('max_connections', 5)
            self.pool = redis.ConnectionPool(
                host=host,
                port=port,
                db=db,
                password=password,
                max_connections=max_connections
            )
            self.redis = redis.Redis(connection_pool=self.pool)
            self.initialized = True
            logger.info(f"Redis client initialized with host: {host}, port: {port}, db: {db}")


    def delete(self, key):
        """删除指定的key"""
        return self.redis.delete(key)


    def exists(self, key):
        """判断key是否存在"""
        return self.redis.exists(key)


    def expire(self, key, expire):
        """设置key的过期时间"""
        return self.redis.expire(key, expire)


    def ttl(self, key):
        """获取key的剩余过期时间"""
        return self.redis.ttl(key)


    def set(self, key, value, expire=None):
        """
        设置key的值,支持过期时间
        :param key: 键
        :param value: 值(会自动序列化)
        :param expire: 过期时间(秒)
        """
        if isinstance(value, (dict, list)):
            value = json.dumps(value)
        if expire:
            return self.redis.setex(key, expire, value)
        return self.redis.set(key, value)


    def get(self, key, default=None):
        """
        获取key的值
        :param key: 键
        :param default: 默认值
        :return: 值(会自动反序列化)
        """
        value = self.redis.get(key)
        if value is None:
            return default
        try:
            return json.loads(value)
        except:
            return value.decode() if isinstance(value, bytes) else value


    def mget(self, keys):
        """
        批量获取多个key的值
        :param keys: 键列表
        :return: 值列表(会自动反序列化)
        """
        values = self.redis.mget(keys)
        return [json.loads(v) if v else None for v in values]


    def mset(self, items):
        """
        批量设置多个key的值
        :param items: 键值对字典
        """
        items = {k: json.dumps(v) if isinstance(v, (dict, list)) else v
                for k, v in items.items()}
        return self.redis.mset(items)


    def hset(self, name, key, value):
        """
        设置哈希表字段的值
        :param name: 哈希表名
        :param key: 字段名
        :param value: 字段值
        """
        if isinstance(value, (dict, list)):
            value = json.dumps(value)
        return self.redis.hset(name, key, value)


    def hget(self, name, key, default=None):
        """
        获取哈希表字段的值
        :param name: 哈希表名
        :param key: 字段名
        :param default: 默认值
        :return: 字段值(会自动反序列化)
        """
        value = self.redis.hget(name, key)
        if value is None:
            return default
        try:
            return json.loads(value)
        except:
            return value.decode() if isinstance(value, bytes) else value


    def hgetall(self, name):
        """
        获取哈希表所有字段和值
        :param name: 哈希表名
        :return: 字段值字典(会自动反序列化)
        """
        data = self.redis.hgetall(name)
        return {k.decode(): json.loads(v) if v else None
               for k, v in data.items()}


    def pipeline(self):
        """
        获取管道对象，用于批量执行命令
        :return: Pipeline对象
        """
        return self.redis.pipeline()


    def transaction(self, func, *args, **kwargs):
        """
        执行事务
        :param func: 事务函数
        :param args: 函数参数
        :param kwargs: 函数关键字参数
        :return: 事务执行结果
        """
        with self.redis.pipeline() as pipe:
            while True:
                try:
                    pipe.watch(*args)
                    func(pipe)
                    pipe.execute()
                    break
                except redis.WatchError:
                    continue


    def lpush(self, name, *values):
        """
        将一个或多个值插入到列表头部
        :param name: 列表名
        :param values: 要插入的值
        :return: 列表长度
        """
        return self.redis.lpush(name, *[json.dumps(v) if isinstance(v, (dict, list)) else v
                                      for v in values])


    def rpush(self, name, *values):
        """
        将一个或多个值插入到列表尾部
        :param name: 列表名
        :param values: 要插入的值
        :return: 列表长度
        """
        return self.redis.rpush(name, *[json.dumps(v) if isinstance(v, (dict, list)) else v
                                      for v in values])


    def lrange(self, name, start, end):
        """
        获取列表指定范围内的元素
        :param name: 列表名
        :param start: 起始位置
        :param end: 结束位置
        :return: 元素列表(会自动反序列化)
        """
        values = self.redis.lrange(name, start, end)
        return [json.loads(v) if v else None for v in values]


    def sadd(self, name, *values):
        """
        向集合添加一个或多个成员
        :param name: 集合名
        :param values: 成员值
        :return: 成功添加的成员数量
        """
        return self.redis.sadd(name, *values)


    def smembers(self, name):
        """
        获取集合中的所有成员
        :param name: 集合名
        :return: 成员集合
        """
        return self.redis.smembers(name)


    def zadd(self, name, mapping):
        """
        向有序集合添加一个或多个成员，或者更新已存在成员的分数
        :param name: 有序集合名
        :param mapping: 成员及其分数的字典
        :return: 成功添加的成员数量
        """
        return self.redis.zadd(name, mapping)


    def zrange(self, name, start, end, withscores=False):
        """
        通过索引区间返回有序集合指定区间内的成员
        :param name: 有序集合名
        :param start: 起始位置
        :param end: 结束位置
        :param withscores: 是否返回分数
        :return: 成员列表
        """
        return self.redis.zrange(name, start, end, withscores=withscores)


    def publish(self, channel, message):
        """
        将消息发送到指定的频道
        :param channel: 频道名
        :param message: 消息内容
        :return: 接收到消息的订阅者数量
        """
        if isinstance(message, (dict, list)):
            message = json.dumps(message)
        return self.redis.publish(channel, message)


    def subscribe(self, *channels):
        """
        订阅给定的一个或多个频道
        :param channels: 频道名
        :return: PubSub对象
        """
        pubsub = self.redis.pubsub()
        pubsub.subscribe(*channels)
        return pubsub


class AsyncRedisClient:
    """异步Redis客户端单例类,提供Redis基本操作的异步封装"""
    _instances = {}


    def __new__(cls, **kwargs):
        """单例模式实现,确保相同配置只创建一个实例"""
        key = (kwargs.get('host', '127.0.0.1'), kwargs.get('port', 6379), kwargs.get('db', 0))
        if key not in cls._instances:
            cls._instances[key] = super(AsyncRedisClient, cls).__new__(cls)
        return cls._instances[key]

    def __init__(self, **kwargs):
        """初始化Redis连接池和客户端"""
        if not hasattr(self, 'initialized'):
            host = kwargs.get('host', '127.0.0.1')
            port = kwargs.get('port', 6379)
            db = kwargs.get('db', 0)
            password = kwargs.get('password', None)
            max_connections = kwargs.get('max_connections', 5)
            self.pool = redis.asyncio.ConnectionPool(
                host=host,
                port=port,
                db=db,
                password=password,
                max_connections=max_connections
            )
            self.redis = AsyncRedis(connection_pool=self.pool)
            self.initialized = True
            logger.info(f"Redis client initialized with host: {host}, port: {port}, db: {db}")
    async def delete(self, key):
        """删除指定的key"""
        return await self.redis.delete(key)

    async def exists(self, key):
        """判断key是否存在"""
        return await self.redis.exists(key)

    async def expire(self, key, expire):
        """设置key的过期时间"""
        return await self.redis.expire(key, expire)

    async def ttl(self, key):
        """获取key的剩余过期时间"""
        return await self.redis.ttl(key)

    async def set(self, key, value, expire=None):
        """
        设置key的值,支持过期时间
        :param key: 键
        :param value: 值(会自动序列化)
        :param expire: 过期时间(秒)
        """
        if isinstance(value, (dict, list)):
            value = json.dumps(value)
        if expire:
            return await self.redis.setex(key, expire, value)
        return await self.redis.set(key, value)

    async def get(self, key, default=None):
        """
        获取key的值
        :param key: 键
        :param default: 默认值
        :return: 值(会自动反序列化)
        """
        value = await self.redis.get(key)
        if value is None:
            return default
        try:
            return json.loads(value)
        except (TypeError, json.JSONDecodeError):
            return value

    async def publish(self, channel, message):
        """
        发布消息到指定频道
        :param channel: 频道名
        :param message: 消息内容
        :return: 接收到消息的订阅者数量
        """
        if isinstance(message, (dict, list)):
            message = json.dumps(message)
        return await self.redis.publish(channel, message)

    async def subscribe(self, *channels):
        """
        订阅给定的一个或多个频道
        :param channels: 频道名
        :return: PubSub对象
        """
        pubsub = self.redis.pubsub()
        await pubsub.subscribe(*channels)
        return pubsub
